// TODO: Maybe split off create, train, and test from _MLP/ to their own directories.


// STL
#include <string>                   // std::string
#include <tuple>                    // std::tie()
#include <vector>                   // std::vector<>

// jScience
#include "jScience/linalg.hpp"      // Matrix<>
#include "jScience/stl/istream.hpp" // read_file()

// stats++
#include "statsxx/data.hpp"         // read_datafile()
#include "statsxx/machine_learning/NeuralNet.hpp" // NEURAL_NET
#include "statsxx/optimization/EA.hpp"            // EAParam

// this
#include "_MLP.hpp"                 // create_MLP(), train_MLP(), test_MLP()
#include "init.hpp"                 // read_param_file()


//
// USAGE: mlp filename_param
//
//     filename_param :: parameters filename
//
//     =========================================================
//
// NOTE: See the example input for parameters.
//
int main(
         int argc,
         char* argv[]
         )
{
    //=========================================================
    // INITIALIZATION
    //=========================================================

    std::string filename_param = std::string(argv[1]);

    // ----- PARAMETERS -----
    std::string         mlp_file;
    // =====
    bool                create;
    // -----
    //bool                create_recurrent;
    // -----
    bool                create_classif;
    // -----
    std::vector<int>    create_architecture;
    int                 create_af_type;
    std::string         create_dbn_file;
    // =====
    bool                train;
    // -----
    int                 train_method;
    // -----
    int                 train_nepoch_min;
    int                 train_nepoch_max;
    // -----
    bool                train_early_stopping;
    // -----
    double              train_lr;
    double              train_lr_min;
    double              train_lr_max;
    // -----
    double              train_momentum;
    double              train_weight_penalty;
    // -----
    double              train_qrprop_u;
    double              train_qrprop_d;
    // -----
    double              train_scg_lambda;
    double              train_scg_sigma;
    double              train_scg_convg_iterfrac;
    double              train_scg_rk_tol;
    // -----
    EAParam             train_EA_param;
    // -----
    std::string         train_X_in_file;
    std::string         train_X_out_file;
    // -----
    int                 train_npts_per_batch;
    // =====
    bool                test;
    // -----
    std::string         test_label_file;
    std::string         test_X_in_file;
    std::string         test_X_out_file;
    // -----
    double              test_cutoff;
    // =====
    std::string         prefix;
    std::tie(
             mlp_file,
             // =====
             create,
             // -----
             create_classif,
             // -----
             create_architecture,
             create_af_type,
             create_dbn_file,
             // =====
             train,
             // -----
             train_method,
             // -----
             train_nepoch_min,
             train_nepoch_max,
             // -----
             train_early_stopping,
             // -----
             train_lr,
             train_lr_min,
             train_lr_max,
             // -----
             train_momentum,
             // -----
             train_weight_penalty,
             // -----
             train_qrprop_u,
             train_qrprop_d,
             // -----
             train_scg_lambda,
             train_scg_sigma,
             train_scg_convg_iterfrac,
             train_scg_rk_tol,
             // -----
             train_EA_param,
             // -----
             train_X_in_file,
             train_X_out_file,
             // -----
             train_npts_per_batch,
             // =====
             test,
             // -----
             test_label_file,
             test_X_in_file,
             test_X_out_file,
             // -----
             test_cutoff,
             // =====
             prefix
             ) = read_param_file(
                                 filename_param
                                 );

    //=========================================================
    // CREATE (OR READ) MLP
    //=========================================================

    // NOTE: This always occurs (create or read-in MLP).
    NEURAL_NET mlp = create_MLP(
                                create,
                                // -----
                                create_dbn_file,
                                // -----
                                create_architecture,
                                create_af_type,
                                create_classif,
                                // -----
                                mlp_file
                                );

    //=========================================================
    // TRAIN
    //=========================================================

    if(train)
    {
        Matrix<double> X_in  = read_datafile(
                                             train_X_in_file
                                             );

        Matrix<double> X_out = read_datafile(
                                             train_X_out_file
                                             );

        mlp = train_MLP(
                        mlp,
                        // -----
                        train_method,
                        //------
                        train_nepoch_min,
                        train_nepoch_max,
                        // -----
                        train_early_stopping,
                        // -----
                        train_lr,
                        train_lr_min,
                        train_lr_max,
                        // -----
                        train_momentum,
                        // -----
                        train_weight_penalty,
                        // -----
                        train_qrprop_u,
                        train_qrprop_d,
                        // -----
                        train_scg_lambda,
                        train_scg_sigma,
                        train_scg_convg_iterfrac,
                        train_scg_rk_tol,
                        // -----
                        train_EA_param,
                        // -----
                        X_in,
                        X_out,
                        // -----
                        train_npts_per_batch,
                        // -----
                        mlp_file
                        );
    }

    //=========================================================
    // TEST
    //=========================================================

    if(test)
    {
        std::vector<std::string> label = read_file<std::string>(
                                                                test_label_file
                                                                );

        Matrix<double> X_in = read_datafile(
                                            test_X_in_file
                                            );

        Matrix<double> X_out = read_datafile(
                                             test_X_out_file
                                             );

        test_MLP(
                 mlp,
                 // -----
                 label,
                 X_in,
                 X_out,
                 // -----
                 test_cutoff,
                 // -----
                 prefix
                 );
    }


    return 0;
}
